﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.IO;
using System.Text;

using PdfCreative.Pdf.Graphics;

namespace Stars
{
    class Program
    {
        static void Main(string[] args)
        {
            // We will create 'HelloWorld.pdf' in the user's temporary folder.
            string path = Path.Combine(Path.GetTempPath(), "Table.pdf");

            // Create a new Graphics instance.
            Graphics g = new Graphics(path);

            // Render the document.
            RenderDocument(g);

            // Close the graphics.
            g.Close();

            // Open the PDF document.
            Process.Start(path);
        }

        private static void RenderDocument(Graphics g)
        {
            DrawHeaderText(g);

            string[,] data = new string[,]
            {
                { "Fruit", "Name", "Description" },
                { "Apple", "Malus domestica", "The apple is the pomaceous fruit of the apple tree, species Malus domestica in the rose family (Rosaceae)."},
                { "Banana", "Musa acuminata", "Banana is the common name for herbaceous plants of the genus Musa and for the fruit they produce."},
                { "Cherry", "Prunus avium", "The cherry is the fruit of many plants of the genus Prunus, and is a fleshy stone fruit."},
                { "Date", "Phoenix dactylifera", "The date palm (Phoenix dactylifera) is a palm in the genus Phoenix, cultivated for its edible sweet fruit."}
            };

            DrawTable(g, data, 300);
        }

        private static void DrawTable(Graphics g, string[,] data, int tableWidth)
        {
            int columns = data.GetLength(1);
            int rows = data.GetLength(0);
            int padding = 5;

            Font font = new Font("Arial");
            Brush background = new SolidBrush(new RgbColour(253, 231, 234));

            // Calculate column widths and row heights.
            int[] widths = CalculateOptimumWidths(g, font, 10f, data, tableWidth);
            int[] heights = CalculateRowHeights(g, font, 10f, data, widths);

            // Set up the table position.
            Rectangle setup = new Rectangle(36f, 90f, 0, 0);

            // Horizontally and vertically center the text.
            StringFormat format = new StringFormat();
            format.VerticalAlignment = VerticalAlignment.Center;
            format.HorizontalAlignment = HorizontalAlignment.Center;

            Rectangle cellBounds = setup;
            for (int row = 0; row < rows; row++)
            {
                for (int column = 0; column < columns; column++)
                {
                    if (widths[column] > 0)
                    {
                        // Set the size of the cell.
                        cellBounds.Resize(widths[column], heights[row]);

                        // Fill the cell and draw the content.
                        g.FillRectangle(background, cellBounds);

                        cellBounds.Expand(padding * -2, padding * -2);
                        g.DrawString(Rgb.BlackBrush, data[row, column],
                            font, 10f, cellBounds, format);
                        cellBounds.Expand(padding * 2, padding * 2);
                    }

                    // Next cell
                    cellBounds.Move(widths[column], 0);
                }

                // Return to first cell, and move one row down.
                cellBounds.Left = setup.Left;
                cellBounds.Move(0, heights[row]);
            }

            DrawBorders(g, rows, columns, setup, widths, heights);
        }

        private static void DrawBorders(
            Graphics g, int rows, int columns,
            Rectangle setup, int[] widths, int[] heights)
        {
            Pen pen = new Pen(Rgb.Black, 0.25f);

            Rectangle cellBounds = setup;
            for (int row = 0; row < rows; row++)
            {
                cellBounds.Height = heights[row];

                for (int column = 0; column < columns; column++)
                {
                    cellBounds.Width = widths[column];

                    // Optionally draw the left hand and top borders.
                    if (row == 0)
                        g.DrawLine(pen,
                            cellBounds.Left, cellBounds.Top,
                            cellBounds.Right, cellBounds.Top);

                    if (column == 0)
                        g.DrawLine(pen,
                            cellBounds.Left, cellBounds.Top,
                            cellBounds.Left, cellBounds.Bottom);

                    // Draw the border on the right-hand side of, and
                    // below, the cell.
                    g.DrawLine(pen,
                        cellBounds.Left, cellBounds.Bottom,
                        cellBounds.Right, cellBounds.Bottom);

                    g.DrawLine(pen,
                        cellBounds.Right, cellBounds.Top,
                        cellBounds.Right, cellBounds.Bottom);

                    // Next cell
                    cellBounds.Move(widths[column], 0);

                }

                // Return to first cell, and move one row down.
                cellBounds.Left = setup.Left;
                cellBounds.Move(0, heights[row]);
            }
        }

        private static int[] CalculateOptimumWidths(Graphics g, Font font, float size, string[,] data, int tableWidth)
        {
            int columns = data.GetLength(1);
            int rows = data.GetLength(0);

            int[] result = new int[columns];

            // Calculate the maximum widths of each column.
            int[] maximums = CalculateWidths(g, font, size, data);

            // If the total exceeds the table width then we need to shorten the columns.
            if (tableWidth < maximums.Sum())
            {
                // Distribute the available space. We don't give a column more space
                // than it really needs. We keep distributing space to remaining
                // columns until nothing is left.
                int available = tableWidth;
                int eligible = columns;
                while (available > 0 && eligible > 0)
                {
                    int increment = available / eligible;

                    for (int column = 0; column < columns; column++)
                    {
                        int increased = Math.Min(increment, maximums[column] - result[column]);
                        if (increased > 0)
                        {
                            result[column] += increased;
                            available -= increased;

                            if (result[column] >= maximums[column]) eligible--;
                        }
                    }
                }
            }
            else
            {
                maximums.CopyTo(result, 0);
            }

            return result;
        }

        private static int[] CalculateRowHeights(Graphics g, Font font, float size, string[,] data, int[] widths)
        {
            int columns = data.GetLength(1);
            int rows = data.GetLength(0);

            int[] result = new int[rows];

            // For each row, find the tallest cell.
            for (int row = 0; row < rows; row++)
            {
                int height = 0;
                for (int column = 0; column < columns; column++)
                {
                    Rectangle cellSize = g.MeasureString(data[row, column], font, size, widths[column] - 10);
                    height = Math.Max((int)Math.Ceiling(cellSize.Height), height);
                }

                result[row] = height + 10; // for padding
            }

            return result;
        }

        private static int[] CalculateWidths(Graphics g, Font font, float size, string[,] data)
        {
            int columns = data.GetLength(1);
            int rows = data.GetLength(0);

            int[] result = new int[columns];

            // For each column, find the widest cell.
            for (int column = 0; column < columns; column++)
            {
                int width = 0;
                for (int row = 0; row < rows; row++)
                {
                    Rectangle cellSize = g.MeasureString(data[row, column], font, size);
                    width = Math.Max((int)cellSize.Width, width);
                }

                result[column] = width + 10; // for padding
            }
            return result;
        }

        private static void DrawHeaderText(Graphics g)
        {
            g.DrawString(
                Rgb.BlackBrush,
                "Sample project: Table",
                new Font("Times New Roman Bold"),
                10f,
                36f,
                36f);

            g.DrawString(
                Rgb.BlackBrush,
                "Render a table with auto-sized columns.",
                new Font("Times New Roman"),
                10f,
                36f,
                50f);
        }
    }
}
